package uy.org.softtech.cssuite.servicios.impl;

import static org.springframework.ui.velocity.VelocityEngineUtils.mergeTemplateIntoString;
import static org.springframework.util.Assert.notNull;
import static org.springframework.util.StringUtils.hasText;

import java.net.URI;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.servlet.http.HttpServletRequest;

import org.apache.http.client.utils.URIBuilder;
import org.apache.log4j.Logger;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.context.MessageSource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import uy.org.softtech.cssuite.core.Controlador.Accion;
import uy.org.softtech.cssuite.core.Servicio;
import uy.org.softtech.cssuite.entidad.usuario.Usuario;
import uy.org.softtech.cssuite.excepciones.NotificacionNoEnviadaException;
import uy.org.softtech.cssuite.servicios.ServiciosCorreoElectronico;
import uy.org.softtech.cssuite.web.modelo.ModeloCredenciales.Paso;

/**
 * Clase que implementa {@link ServiciosCorreoElectronico}.
 * 
 * @author fabian.lobo
 * @see ServiciosCorreoElectronico
 */
@Service(value = "servicios-correo")
public class ServiciosCorreoElectronicoImpl extends Servicio implements ServiciosCorreoElectronico {
	
	private Logger log = Logger.getLogger(getClass());
	
	@Resource(name = "servidor-correo")
	private JavaMailSender servidorCorreo;
	
	@Resource(name = "mensaje-correo")
	private SimpleMailMessage mensajeCorreo;
	
	@Resource(name = "generador-mensajes")
	private VelocityEngine generadorTemplate;
	
	@Resource(name = "fuente-mensajes")
	private MessageSource fuenteMensajes;
	
	/**
	 * Método para verificar que todos los parámetros han sido seteados correctamente.
	 * 
	 * @author fabian.lobo
	 */
	@PostConstruct
	public void inicializacion(){
		notNull(servidorCorreo(), "La propiedad [servidorCorreo] de [" + getClass().getName() + "] no ha sido establecida.");
		notNull(mensajeCorreo(), "La propiedad [mensajeCorreo] de [" + getClass().getName() + "] no ha sido establecida.");
		notNull(generadorTemplate(), "La propiedad [generadorTemplate] de [" + getClass().getName() + "] no ha sido establecida.");
		notNull(fuenteMensajes(), "La propiedad [fuenteMensajes] de [" + getClass().getName() + "] no ha sido establecida.");
		
		log.info("Implementación de los servicios relacionados a las notificaciones por correo electrónico ha sido inicializado correctamente.");
	}

	@Override
	public void notificarSolicitudCambioCredenciales(Usuario usuario, String token, Locale locale) throws NotificacionNoEnviadaException {
			
		//1. Asunto del mensaje
		String asunto = fuenteMensajes().getMessage("notificacion.cambioCredenciales.asunto", null, "Fue solicitado un nuevo proceso para el cambio de sus credenciales.", locale);
		
		Map<String, Object> modelo = new HashMap<String, Object>();
		
		//2. Saludo
		modelo.put("NOMBRE", usuario.convierteNombreCompleto());
		
		//3. Link para continuar el proceso
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
				
		URIBuilder builder = new URIBuilder(URI.create(request.getRequestURL().toString()));
		builder.setPath(request.getContextPath() + "/credenciales.do").addParameter("accion", Accion.CREDENCIALES.toString()).addParameter("paso", Paso.RESTABLECER.toString()).addParameter("token", token);
		String url = builder.toString();
		
		modelo.put("LINK_PROCESO", url);
		
		//4. Texto de reclamaciones
		builder.setPath(request.getContextPath() + "/reclamos.do").removeQuery();
		builder.addParameter("accion", Accion.CREDENCIALES.toString()).addParameter("paso", Paso.SOLICITAR.toString()).addParameter("usuario", usuario.getUsuario());
		url = builder.toString();
		
		modelo.put("LINK_RECLAMACIONES", url);
		
		//5. Nombre del template
		String template = "cambio-credenciales.vm";
		
		//6. Envío del mensaje
		String direccion = hasText(usuario.getCorreoElectronico()) ? usuario.getCorreoElectronico() : usuario.getUsuario();
		enviarCorreoElectronico(direccion, asunto, modelo, template, locale);
	}
	
	/**
	 * Metodo que envía el correo electrónico utilizando el servidor configurado
	 * 
	 * @param correoElectronico - Dirección de correo electrónico que recibe la notificación
	 * @param asunto - Texto a aparecer en el Asunto
	 * @param modelo - Mapa conteniendo los datos a insertar en el template
	 * @param nombreTemplate - Ubicación del template a utilizar
	 * @throws NotificacionNoEnviadaException si la notificación no pudo ser enviada.
	 */
	private void enviarCorreoElectronico(String correoElectronico, String asunto, Map<String, Object> modelo, String nombreTemplate, Locale locale) throws NotificacionNoEnviadaException {
		try {
			MimeMessage mensajeCorreoElectronico = servidorCorreo().createMimeMessage();
			mensajeCorreoElectronico.setRecipient(RecipientType.TO, new InternetAddress(correoElectronico));
			mensajeCorreoElectronico.setRecipient(RecipientType.CC, new InternetAddress(mensajeCorreo().getFrom()));
			mensajeCorreoElectronico.setRecipient(RecipientType.BCC, new InternetAddress(mensajeCorreo().getFrom()));
			mensajeCorreoElectronico.setFrom(new InternetAddress(mensajeCorreo().getFrom()));
			mensajeCorreoElectronico.setReplyTo(InternetAddress.parse(mensajeCorreo().getReplyTo()));
			mensajeCorreoElectronico.setSubject(asunto);
			
			String template = "templates/" + locale.getLanguage().toLowerCase() + "/" + nombreTemplate;
			mensajeCorreoElectronico.setText(mergeTemplateIntoString(generadorTemplate(), template, "UTF-8", modelo), "UTF-8", "html");
			servidorCorreo().send(mensajeCorreoElectronico);
			
		} catch (AddressException e) {
			log.error(e);
			throw new NotificacionNoEnviadaException("La notificación a " + correoElectronico + " no pudo ser enviada");
		} catch (MessagingException e) {
			log.error(e);
			throw new NotificacionNoEnviadaException("La notificación a " + correoElectronico + " no pudo ser enviada");
		}
	}

	/*
	 *  GETTERS
	 */	
	public JavaMailSender servidorCorreo() {
		return servidorCorreo;
	}
	public SimpleMailMessage mensajeCorreo() {
		return mensajeCorreo;
	}
	public VelocityEngine generadorTemplate() {
		return generadorTemplate;
	}
	public MessageSource fuenteMensajes() {
		return fuenteMensajes;
	}
}
